home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 001-025 / disk_022 / lemacs / search.c < prev    next >
C/C++ Source or Header  |  1992-05-06  |  13KB  |  551 lines

  1. /*
  2.  * The functions in this file implement commands that search in the forward
  3.  * and backward directions. There are no special characters in the search
  4.  * strings. Probably should have a regular expression search, or something
  5.  * like that.
  6.  *
  7.  */
  8.  
  9. #include        <stdio.h>
  10. #include    "estruct.h"
  11. #include        "edef.h"
  12.  
  13. /*
  14.  * Search forward. Get a search string from the user, and search, beginning at
  15.  * ".", for the string. If found, reset the "." to be just after the match
  16.  * string, and [perhaps] repaint the display. Bound to "C-S".
  17.  */
  18.  
  19. /*    string search input parameters    */
  20.  
  21. #define    PTBEG    1    /* leave the point at the begining on search */
  22. #define    PTEND    2    /* leave the point at the end on search */
  23.  
  24. forwsearch(f, n)
  25.  
  26. {
  27.     register int status;
  28.  
  29.     /* resolve the repeat count */
  30.     if (n == 0)
  31.         n = 1;
  32.     if (n < 1)    /* search backwards */
  33.         return(backsearch(f, -n));
  34.  
  35.     /* ask the user for the text of a pattern */
  36.     if ((status = readpattern("Search")) != TRUE)
  37.         return(status);
  38.  
  39.     /* search for the pattern */
  40.     while (n-- > 0) {
  41.         if ((status = forscan(&pat[0],PTEND)) == FALSE)
  42.             break;
  43.     }
  44.  
  45.     /* and complain if not there */
  46.     if (status == FALSE)
  47.         mlwrite("Not found");
  48.     return(status);
  49. }
  50.  
  51. forwhunt(f, n)
  52.  
  53. {
  54.     register int status;
  55.  
  56.     /* resolve the repeat count */
  57.     if (n == 0)
  58.         n = 1;
  59.     if (n < 1)    /* search backwards */
  60.         return(backhunt(f, -n));
  61.  
  62.     /* Make sure a pattern exists */
  63.     if (pat[0] == 0) {
  64.         mlwrite("No pattern set");
  65.         return(FALSE);
  66.     }
  67.  
  68.     /* search for the pattern */
  69.     while (n-- > 0) {
  70.         if ((status = forscan(&pat[0],PTEND)) == FALSE)
  71.             break;
  72.     }
  73.  
  74.     /* and complain if not there */
  75.     if (status == FALSE)
  76.         mlwrite("Not found");
  77.     return(status);
  78. }
  79.  
  80. /*
  81.  * Reverse search. Get a search string from the user, and search, starting at
  82.  * "." and proceeding toward the front of the buffer. If found "." is left
  83.  * pointing at the first character of the pattern [the last character that was
  84.  * matched]. Bound to "C-R".
  85.  */
  86. backsearch(f, n)
  87.  
  88. {
  89.     register int s;
  90.  
  91.     /* resolve null and negative arguments */
  92.     if (n == 0)
  93.         n = 1;
  94.     if (n < 1)
  95.         return(forwsearch(f, -n));
  96.  
  97.     /* get a pattern to search */
  98.     if ((s = readpattern("Reverse search")) != TRUE)
  99.         return(s);
  100.  
  101.     /* and go search for it */
  102.     bsearch(f,n);
  103. }
  104.  
  105. backhunt(f, n)    /* hunt backward for the last search string entered */
  106.  
  107. {
  108.     /* resolve null and negative arguments */
  109.     if (n == 0)
  110.         n = 1;
  111.     if (n < 1)
  112.         return(forwhunt(f, -n));
  113.  
  114.     /* Make sure a pattern exists */
  115.     if (pat[0] == 0) {
  116.         mlwrite("No pattern set");
  117.         return(FALSE);
  118.     }
  119.  
  120.     /* and go search for it */
  121.     bsearch(f,n);
  122. }
  123.  
  124. bsearch(f, n)
  125.  
  126. {
  127.     register LINE *clp;
  128.     register int cbo;
  129.     register LINE *tlp;
  130.     register int tbo;
  131.     register int c;
  132.     register char *epp;
  133.     register char *pp;
  134.  
  135.     /* find a pointer to the end of the pattern */
  136.     for (epp = &pat[0]; epp[1] != 0; ++epp)
  137.         ;
  138.  
  139.     /* make local copies of the starting location */
  140.     clp = curwp->w_dotp;
  141.     cbo = curwp->w_doto;
  142.  
  143.     while (n-- > 0) {
  144.         for (;;) {
  145.             /* if we are at the begining of the line, wrap back around */
  146.             if (cbo == 0) {
  147.                 clp = lback(clp);
  148.  
  149.                 if (clp == curbp->b_linep) {
  150.                     mlwrite("Not found");
  151.                     return(FALSE);
  152.                 }
  153.  
  154.                 cbo = llength(clp)+1;
  155.             }
  156.  
  157.             /* fake the <NL> at the end of a line */
  158.             if (--cbo == llength(clp))
  159.                 c = '\n';
  160.             else
  161.                 c = lgetc(clp, cbo);
  162.  
  163.             /* check for a match against the end of the pattern */
  164.             if (eq(c, *epp) != FALSE) {
  165.                 tlp = clp;
  166.                 tbo = cbo;
  167.                 pp  = epp;
  168.  
  169.                 /* scanning backwards through the rest of the
  170.                    pattern looking for a match            */
  171.                 while (pp != &pat[0]) {
  172.                     /* wrap across a line break */
  173.                     if (tbo == 0) {
  174.                         tlp = lback(tlp);
  175.                         if (tlp == curbp->b_linep)
  176.                             goto fail;
  177.  
  178.                         tbo = llength(tlp)+1;
  179.                     }
  180.  
  181.                     /* fake the <NL> */
  182.                     if (--tbo == llength(tlp))
  183.                         c = '\n';
  184.                     else
  185.                         c = lgetc(tlp, tbo);
  186.  
  187.                     if (eq(c, *--pp) == FALSE)
  188.                         goto fail;
  189.                 }
  190.  
  191.                 /* A Match!  reset the current cursor */
  192.                 curwp->w_dotp  = tlp;
  193.                 curwp->w_doto  = tbo;
  194.                 curwp->w_flag |= WFMOVE;
  195.                 goto next;
  196.             }
  197. fail:;
  198.         }
  199. next:;
  200.     }
  201.     return(TRUE);
  202. }
  203.  
  204. /*
  205.  * Compare two characters. The "bc" comes from the buffer. It has it's case
  206.  * folded out. The "pc" is from the pattern.
  207.  */
  208. eq(bc, pc)
  209.     int bc;
  210.     int pc;
  211.  
  212. {
  213.     if ((curwp->w_bufp->b_mode & MDEXACT) == 0) {
  214.         if (bc>='a' && bc<='z')
  215.             bc -= 0x20;
  216.  
  217.         if (pc>='a' && pc<='z')
  218.             pc -= 0x20;
  219.     }
  220.  
  221.     if (bc == pc)
  222.         return(TRUE);
  223.  
  224.     return(FALSE);
  225. }
  226.  
  227. /*
  228.  * Read a pattern. Stash it in the external variable "pat". The "pat" is not
  229.  * updated if the user types in an empty line. If the user typed an empty line,
  230.  * and there is no old pattern, it is an error. Display the old pattern, in the
  231.  * style of Jeff Lomicka. There is some do-it-yourself control expansion.
  232.  * change to using <ESC> to delemit the end-of-pattern to allow <NL>s in
  233.  * the search string.
  234.  */
  235. readpattern(prompt)
  236.  
  237. char *prompt;
  238.  
  239. {
  240.     register int s;
  241.     char tpat[NPAT+20];
  242.  
  243.     strcpy(tpat, prompt);    /* copy prompt to output string */
  244.     strcat(tpat, " [");    /* build new prompt string */
  245.     expandp(&pat[0], &tpat[strlen(tpat)], NPAT/2);    /* add old pattern */
  246.     strcat(tpat, "]<ESC>: ");
  247.  
  248.     s = mlreplyt(tpat, tpat, NPAT, 27);    /* Read pattern */
  249.  
  250.     if (s == TRUE)                /* Specified */
  251.         strcpy(pat, tpat);
  252.     else if (s == FALSE && pat[0] != 0)    /* CR, but old one */
  253.         s = TRUE;
  254.  
  255.     return(s);
  256. }
  257.  
  258. sreplace(f, n)    /*    Search and replace (ESC-R)    */
  259.  
  260. int f;        /* default flag */
  261. int n;        /* # of repetitions wanted */
  262.  
  263. {
  264.     return(replaces(FALSE, f, n));
  265. }
  266.  
  267. qreplace(f, n)    /*    search and replace with query (ESC-CTRL-R)    */
  268.  
  269. int f;        /* default flag */
  270. int n;        /* # of repetitions wanted */
  271.  
  272. {
  273.     return(replaces(TRUE, f, n));
  274. }
  275.  
  276. /*    replaces:    search for a string and replace it with another
  277.             string. query might be enabled (according to
  278.             kind).                        */
  279. replaces(kind, f, n)
  280.  
  281. int kind;    /* Query enabled flag */
  282. int f;        /* default flag */
  283. int n;        /* # of repetitions wanted */
  284.  
  285. {
  286.     register int i;        /* loop index */
  287.     register int s;        /* success flag on pattern inputs */
  288.     register int slength,
  289.              rlength;    /* length of search and replace strings */
  290.     register int numsub;    /* number of substitutions */
  291.     register int nummatch;    /* number of found matches */
  292.     int nlflag;        /* last char of search string a <NL>? */
  293.     int nlrepl;        /* was a replace done on the last line? */
  294.     char tmpc;        /* temporary character */
  295.     char c;            /* input char for query */
  296.     char tpat[NPAT];    /* temporary to hold search pattern */
  297.     LINE *origline;        /* original "." position */
  298.     int origoff;        /* and offset (for . query option) */
  299.  
  300.     if (curbp->b_mode&MDVIEW)    /* don't allow this command if    */
  301.         return(rdonly());    /* we are in read only mode    */
  302.  
  303.     /* check for negative repititions */
  304.     if (f && n < 0)
  305.         return(FALSE);
  306.  
  307.     /* ask the user for the text of a pattern */
  308.     if ((s = readpattern(
  309.         (kind == FALSE ? "Replace" : "Query replace"))) != TRUE)
  310.         return(s);
  311.     strcpy(&tpat[0], &pat[0]);    /* salt it away */
  312.  
  313.     /* ask for the replacement string */
  314.     strcpy(&pat[0], &rpat[0]);    /* set up default string */
  315.     if ((s = readpattern("with")) == ABORT)
  316.         return(s);
  317.  
  318.     /* move everything to the right place and length them */
  319.     strcpy(&rpat[0], &pat[0]);
  320.     strcpy(&pat[0], &tpat[0]);
  321.     slength = strlen(&pat[0]);
  322.     rlength = strlen(&rpat[0]);
  323.  
  324.     /* set up flags so we can make sure not to do a recursive
  325.        replace on the last line */
  326.     nlflag = (pat[slength - 1] == '\n');
  327.     nlrepl = FALSE;
  328.  
  329.     /* build query replace question string */
  330.     strcpy(tpat, "Replace '");
  331.     expandp(&pat[0], &tpat[strlen(tpat)], NPAT/3);
  332.     strcat(tpat, "' with '");
  333.     expandp(&rpat[0], &tpat[strlen(tpat)], NPAT/3);
  334.     strcat(tpat, "'? ");
  335.  
  336.     /* save original . position */
  337.     origline = curwp->w_dotp;
  338.     origoff = curwp->w_doto;
  339.  
  340.     /* scan through the file */
  341.     numsub = 0;
  342.     nummatch = 0;
  343.     while ((f == FALSE || n > nummatch) &&
  344.         (nlflag == FALSE || nlrepl == FALSE)) {
  345.  
  346.         /* search for the pattern */
  347.         if (forscan(&pat[0],PTBEG) != TRUE)
  348.             break;        /* all done */
  349.         ++nummatch;    /* increment # of matches */
  350.  
  351.         /* check if we are on the last line */
  352.         nlrepl = (lforw(curwp->w_dotp) == curwp->w_bufp->b_linep);
  353.         
  354.         /* check for query */
  355.         if (kind) {
  356.             /* get the query */
  357.             mlwrite(&tpat[0], &pat[0], &rpat[0]);
  358. qprompt:
  359.             update();  /* show the proposed place to change */
  360.             c = (*term.t_getchar)();    /* and input */
  361.             mlwrite("");            /* and clear it */
  362.  
  363.             /* and respond appropriately */
  364.             switch (c) {
  365.                 case 'y':    /* yes, substitute */
  366.                 case ' ':
  367.                         break;
  368.  
  369.                 case 'n':    /* no, onword */
  370.                         forwchar(FALSE, 1);
  371.                         continue;
  372.  
  373.                 case '!':    /* yes/stop asking */
  374.                         kind = FALSE;
  375.                         break;
  376.  
  377.                 case '.':    /* abort! and return */
  378.                         /* restore old position */
  379.                         curwp->w_dotp = origline;
  380.                         curwp->w_doto = origoff;
  381.                         curwp->w_flag |= WFMOVE;
  382.  
  383.                 case BELL:    /* abort! and stay */
  384.                         mlwrite("Aborted!");
  385.                         return(FALSE);
  386.  
  387.                 default:    /* bitch and beep */
  388.                         (*term.t_beep)();
  389.  
  390.                 case '?':    /* help me */
  391. mlwrite("(Y)es, (N)o, (!)Do the rest, (^G)Abort, (.)Abort back, (?)Help: ");
  392.                         goto qprompt;
  393.  
  394.             }
  395.         }
  396.  
  397.         /* delete the sucker */
  398.         if (ldelete(slength, FALSE) != TRUE) {
  399.             /* error while deleting */
  400.             mlwrite("ERROR while deleteing");
  401.             return(FALSE);
  402.         }
  403.  
  404.         /* and insert its replacement */
  405.         for (i=0; i<rlength; i++) {
  406.             tmpc = rpat[i];
  407.             s = (tmpc == '\n' ? lnewline() : linsert(1, tmpc));
  408.             if (s != TRUE) {
  409.                 /* error while inserting */
  410.                 mlwrite("Out of memory while inserting");
  411.                 return(FALSE);
  412.             }
  413.         }
  414.  
  415.         numsub++;    /* increment # of substitutions */
  416.     }
  417.  
  418.     /* and report the results */
  419.     mlwrite("%d substitutions",numsub);
  420.     return(TRUE);
  421. }
  422.  
  423. forscan(patrn,leavep)    /*    search forward for a <patrn>    */
  424.  
  425. char *patrn;        /* string to scan for */
  426. int leavep;        /* place to leave point
  427.                 PTBEG = begining of match
  428.                 PTEND = at end of match        */
  429.  
  430. {
  431.     register LINE *curline;        /* current line during scan */
  432.     register int curoff;        /* position within current line */
  433.     register LINE *lastline;    /* last line position during scan */
  434.     register int lastoff;        /* position within last line */
  435.     register int c;            /* character at current position */
  436.     register LINE *matchline;    /* current line during matching */
  437.     register int matchoff;        /* position in matching line */
  438.     register char *patptr;        /* pointer into pattern */
  439.  
  440.     /* setup local scan pointers to global "." */
  441.  
  442.     curline = curwp->w_dotp;
  443.     curoff = curwp->w_doto;
  444.  
  445.     /* scan each character until we hit the head link record */
  446.  
  447.     while (curline != curbp->b_linep) {
  448.  
  449.         /* save the current position in case we need to
  450.            restore it on a match            */
  451.  
  452.         lastline = curline;
  453.         lastoff = curoff;
  454.  
  455.         /* get the current character resolving EOLs */
  456.  
  457.         if (curoff == llength(curline)) {    /* if at EOL */
  458.             curline = lforw(curline);    /* skip to next line */
  459.             curoff = 0;
  460.             c = '\n';            /* and return a <NL> */
  461.         } else
  462.             c = lgetc(curline, curoff++);    /* get the char */
  463.  
  464.         /* test it against first char in pattern */
  465.         if (eq(c, patrn[0]) != FALSE) {    /* if we find it..*/
  466.             /* setup match pointers */
  467.             matchline = curline;
  468.             matchoff = curoff;
  469.             patptr = &patrn[0];
  470.  
  471.             /* scan through patrn for a match */
  472.             while (*++patptr != 0) {
  473.                 /* advance all the pointers */
  474.                 if (matchoff == llength(matchline)) {
  475.                     /* advance past EOL */
  476.                     matchline = lforw(matchline);
  477.                     matchoff = 0;
  478.                     c = '\n';
  479.                 } else
  480.                     c = lgetc(matchline, matchoff++);
  481.  
  482.                 /* and test it against the pattern */
  483.                 if (eq(*patptr, c) == FALSE)
  484.                     goto fail;
  485.             }
  486.  
  487.             /* A SUCCESSFULL MATCH!!! */
  488.             /* reset the global "." pointers */
  489.             if (leavep == PTEND) {    /* at end of string */
  490.                 curwp->w_dotp = matchline;
  491.                 curwp->w_doto = matchoff;
  492.             } else {        /* at begining of string */
  493.                 curwp->w_dotp = lastline;
  494.                 curwp->w_doto = lastoff;
  495.             }
  496.             curwp->w_flag |= WFMOVE; /* flag that we have moved */
  497.             return(TRUE);
  498.  
  499.         }
  500. fail:;            /* continue to search */
  501.     }
  502.  
  503.     /* we could not find a match */
  504.  
  505.     return(FALSE);
  506. }
  507.  
  508. /*     expandp:    expand control key sequences for output        */
  509.  
  510. expandp(srcstr, deststr, maxlength)
  511.  
  512. char *srcstr;    /* string to expand */
  513. char *deststr;    /* destination of expanded string */
  514. int maxlength;    /* maximum chars in destination */
  515.  
  516. {
  517.     char c;        /* current char to translate */
  518.  
  519.     /* scan through the string */
  520.     while ((c = *srcstr++) != 0) {
  521.         if (c == '\n') {        /* its an EOL */
  522.             *deststr++ = '<';
  523.             *deststr++ = 'N';
  524.             *deststr++ = 'L';
  525.             *deststr++ = '>';
  526.             maxlength -= 4;
  527.         } else if (c < 0x20 || c == 0x7f) {    /* control character */
  528.             *deststr++ = '^';
  529.             *deststr++ = c ^ 0x40;
  530.             maxlength -= 2;
  531.         } else if (c == '%') {
  532.             *deststr++ = '%';
  533.             *deststr++ = '%';
  534.             maxlength -= 2;
  535.  
  536.         } else {            /* any other character */
  537.             *deststr++ = c;
  538.             maxlength--;
  539.         }
  540.  
  541.         /* check for maxlength */
  542.         if (maxlength < 4) {
  543.             *deststr++ = '$';
  544.             *deststr = '\0';
  545.             return(FALSE);
  546.         }
  547.     }
  548.     *deststr = '\0';
  549.     return(TRUE);
  550. }
  551.